1 module d_snprintf.test; 2 3 // version = SNPRINTF_TEST; 4 5 version (SNPRINTF_TEST) { 6 7 import d_snprintf; 8 9 import core.stdc.math : pow; 10 import core.stdc.string : strcmp; 11 import core.stdc.stdlib : malloc; 12 import core.stdc.stdio : sprintf, FILE, fwrite; 13 14 nothrow: 15 @nogc: 16 17 version(D_BetterC) { 18 version (Windows) { 19 // Workaround for https://issues.dlang.org/show_bug.cgi?id=18816 and https://issues.dlang.org/show_bug.cgi?id=19933 20 private extern extern(C) FILE* __acrt_iob_func(int); 21 shared FILE* stdout; 22 } else { 23 import core.stdc.stdio : stdout; 24 } 25 } else { 26 import core.stdc.stdio : stdout; 27 } 28 29 enum INT_MIN = int.min; 30 enum INT_MAX = int.max; 31 enum LONG_MIN = long.max; 32 enum LONG_MAX = long.min; 33 enum UINT_MAX = uint.max; 34 35 // wrappers for file writing and memory allocation 36 void write_file(void* p_file, ubyte[] data) { 37 fwrite(data.ptr, 1, data.length, cast(FILE*)p_file); 38 } 39 40 void* alloc_func(size_t size) { 41 return malloc(size); 42 } 43 44 // Definitions of printf, fprintf and asprintf based on our wrappers 45 alias asprintf = rpl_asprintf!alloc_func; 46 alias vasprintf = rpl_vasprintf!alloc_func; 47 48 int fprintf(A...)(FILE* file, string format, A a) { 49 mixin va_start!a; 50 return rpl_vfprintf!write_file(cast(void*)file, format, va_args); 51 } 52 53 int vfprintf(FILE* file, string format, va_list ap) { 54 return rpl_vfprintf!write_file(cast(void*)file, format, ap); 55 } 56 57 int printf(A...)(string format, A a) { 58 mixin va_start!a; 59 return rpl_vfprintf!write_file(cast(void*)stdout, format, va_args); 60 } 61 62 int vprintf(string format, va_list ap) { 63 return rpl_vfprintf!write_file(cast(void*)stdout, format, ap); 64 } 65 66 // Programm to test this version of snprintf against the stdio version 67 // NOTE: Their will always be differences due to 68 // - floating-point accuracy (e.q. 9.899999895424116 vs 9.899999895424115) 69 // - varying feature support (e.q. no support for the ' prefix on Windows) 70 // - "implementation dependent" formats (e.q. pointers, 00007FF666AECBC0 vs 0x7ff666aecbc0) 71 version(D_BetterC) { 72 extern(C) int main() { 73 version (Windows) { 74 // Workaround for https://issues.dlang.org/show_bug.cgi?id=18816 and https://issues.dlang.org/show_bug.cgi?id=19933 75 stdout = __acrt_iob_func(1); 76 } 77 return test(); 78 } 79 } else { 80 int main() { 81 return test(); 82 } 83 } 84 85 int test() { 86 __gshared const string[] float_fmt = [ 87 /* "%E" and "%e" formats. */ 88 "%.16e", 89 "%22.16e", 90 "%022.16e", 91 "%-22.16e", 92 "%#+'022.16e", 93 "foo|%#+0123.9E|bar", 94 "%-123.9e", 95 "%123.9e", 96 "%+23.9e", 97 "%+05.8e", 98 "%-05.8e", 99 "%05.8e", 100 "%+5.8e", 101 "%-5.8e", 102 "% 5.8e", 103 "%5.8e", 104 "%+4.9e", 105 "%+#010.0e", 106 "%#10.1e", 107 "%10.5e", 108 "% 10.5e", 109 "%5.0e", 110 "%5.e", 111 "%#5.0e", 112 "%#5.e", 113 "%3.2e", 114 "%3.1e", 115 "%-1.5e", 116 "%1.5e", 117 "%01.3e", 118 "%1.e", 119 "%.1e", 120 "%#.0e", 121 "%+.0e", 122 "% .0e", 123 "%.0e", 124 "%#.e", 125 "%+.e", 126 "% .e", 127 "%.e", 128 "%4e", 129 "%e", 130 "%E", 131 /* "%F" and "%f" formats. */ 132 "% '022f", 133 "%+'022f", 134 "%-'22f", 135 "%'22f", 136 "%.16f", 137 "%22.16f", 138 "%022.16f", 139 "%-22.16f", 140 "%#+'022.16f", 141 "foo|%#+0123.9F|bar", 142 "%-123.9f", 143 "%123.9f", 144 "%+23.9f", 145 "%+#010.0f", 146 "%#10.1f", 147 "%10.5f", 148 "% 10.5f", 149 "%+05.8f", 150 "%-05.8f", 151 "%05.8f", 152 "%+5.8f", 153 "%-5.8f", 154 "% 5.8f", 155 "%5.8f", 156 "%5.0f", 157 "%5.f", 158 "%#5.0f", 159 "%#5.f", 160 "%+4.9f", 161 "%3.2f", 162 "%3.1f", 163 "%-1.5f", 164 "%1.5f", 165 "%01.3f", 166 "%1.f", 167 "%.1f", 168 "%#.0f", 169 "%+.0f", 170 "% .0f", 171 "%.0f", 172 "%#.f", 173 "%+.f", 174 "% .f", 175 "%.f", 176 "%4f", 177 "%f", 178 "%F", 179 /* "%G" and "%g" formats. */ 180 "% '022g", 181 "%+'022g", 182 "%-'22g", 183 "%'22g", 184 "%.16g", 185 "%22.16g", 186 "%022.16g", 187 "%-22.16g", 188 "%#+'022.16g", 189 "foo|%#+0123.9G|bar", 190 "%-123.9g", 191 "%123.9g", 192 "%+23.9g", 193 "%+05.8g", 194 "%-05.8g", 195 "%05.8g", 196 "%+5.8g", 197 "%-5.8g", 198 "% 5.8g", 199 "%5.8g", 200 "%+4.9g", 201 "%+#010.0g", 202 "%#10.1g", 203 "%10.5g", 204 "% 10.5g", 205 "%5.0g", 206 "%5.g", 207 "%#5.0g", 208 "%#5.g", 209 "%3.2g", 210 "%3.1g", 211 "%-1.5g", 212 "%1.5g", 213 "%01.3g", 214 "%1.g", 215 "%.1g", 216 "%#.0g", 217 "%+.0g", 218 "% .0g", 219 "%.0g", 220 "%#.g", 221 "%+.g", 222 "% .g", 223 "%.g", 224 "%4g", 225 "%g", 226 "%G", 227 ]; 228 __gshared const float[] float_val = [ 229 -4.136, 230 -134.52, 231 -5.04030201, 232 -3410.01234, 233 -999999.999999, 234 -913450.29876, 235 -913450.2, 236 -91345.2, 237 -9134.2, 238 -913.2, 239 -91.2, 240 -9.2, 241 -9.9, 242 4.136, 243 134.52, 244 5.04030201, 245 3410.01234, 246 999999.999999, 247 913450.29876, 248 913450.2, 249 91345.2, 250 9134.2, 251 913.2, 252 91.2, 253 9.2, 254 9.9, 255 9.96, 256 9.996, 257 9.9996, 258 9.99996, 259 9.999996, 260 9.9999996, 261 9.99999996, 262 0.99999996, 263 0.99999999, 264 0.09999999, 265 0.00999999, 266 0.00099999, 267 0.00009999, 268 0.00000999, 269 0.00000099, 270 0.00000009, 271 0.00000001, 272 0.0000001, 273 0.000001, 274 0.00001, 275 0.0001, 276 0.001, 277 0.01, 278 0.1, 279 1.0, 280 1.5, 281 -1.5, 282 -1.0, 283 -0.1, 284 float.infinity, 285 -float.infinity, 286 float.nan, 287 0 288 ]; 289 __gshared const string[] long_fmt = [ 290 "foo|%0123ld|bar", 291 "% '0123ld", 292 "%+'0123ld", 293 "%-'123ld", 294 "%'123ld", 295 "%123.9ld", 296 "% 123.9ld", 297 "%+123.9ld", 298 "%-123.9ld", 299 "%0123ld", 300 "% 0123ld", 301 "%+0123ld", 302 "%-0123ld", 303 "%10.5ld", 304 "% 10.5ld", 305 "%+10.5ld", 306 "%-10.5ld", 307 "%010ld", 308 "% 010ld", 309 "%+010ld", 310 "%-010ld", 311 "%4.2ld", 312 "% 4.2ld", 313 "%+4.2ld", 314 "%-4.2ld", 315 "%04ld", 316 "% 04ld", 317 "%+04ld", 318 "%-04ld", 319 "%5.5ld", 320 "%+22.33ld", 321 "%01.3ld", 322 "%1.5ld", 323 "%-1.5ld", 324 "%44ld", 325 "%4ld", 326 "%4.0ld", 327 "%4.ld", 328 "%.44ld", 329 "%.4ld", 330 "%.0ld", 331 "%.ld", 332 "%ld", 333 ]; 334 __gshared const int[] long_val = [ 335 INT_MAX, 336 INT_MIN, 337 - 91340, 338 91340, 339 341, 340 134, 341 131, 342 -1, 343 1, 344 0 345 ]; 346 __gshared const string[] ulong_fmt = [ 347 /* "%u" formats. */ 348 "foo|%0123lu|bar", 349 "% '0123lu", 350 "%+'0123lu", 351 "%-'123lu", 352 "%'123lu", 353 "%123.9lu", 354 "% 123.9lu", 355 "%+123.9lu", 356 "%-123.9lu", 357 "%0123lu", 358 "% 0123lu", 359 "%+0123lu", 360 "%-0123lu", 361 "%5.5lu", 362 "%+22.33lu", 363 "%01.3lu", 364 "%1.5lu", 365 "%-1.5lu", 366 "%44lu", 367 "%lu", 368 /* "%o" formats. */ 369 "foo|%#0123lo|bar", 370 "%#123.9lo", 371 "%# 123.9lo", 372 "%#+123.9lo", 373 "%#-123.9lo", 374 "%#0123lo", 375 "%# 0123lo", 376 "%#+0123lo", 377 "%#-0123lo", 378 "%#5.5lo", 379 "%#+22.33lo", 380 "%#01.3lo", 381 "%#1.5lo", 382 "%#-1.5lo", 383 "%#44lo", 384 "%#lo", 385 "%123.9lo", 386 "% 123.9lo", 387 "%+123.9lo", 388 "%-123.9lo", 389 "%0123lo", 390 "% 0123lo", 391 "%+0123lo", 392 "%-0123lo", 393 "%5.5lo", 394 "%+22.33lo", 395 "%01.3lo", 396 "%1.5lo", 397 "%-1.5lo", 398 "%44lo", 399 "%lo", 400 /* "%X" and "%x" formats. */ 401 "foo|%#0123lX|bar", 402 "%#123.9lx", 403 "%# 123.9lx", 404 "%#+123.9lx", 405 "%#-123.9lx", 406 "%#0123lx", 407 "%# 0123lx", 408 "%#+0123lx", 409 "%#-0123lx", 410 "%#5.5lx", 411 "%#+22.33lx", 412 "%#01.3lx", 413 "%#1.5lx", 414 "%#-1.5lx", 415 "%#44lx", 416 "%#lx", 417 "%#lX", 418 "%123.9lx", 419 "% 123.9lx", 420 "%+123.9lx", 421 "%-123.9lx", 422 "%0123lx", 423 "% 0123lx", 424 "%+0123lx", 425 "%-0123lx", 426 "%5.5lx", 427 "%+22.33lx", 428 "%01.3lx", 429 "%1.5lx", 430 "%-1.5lx", 431 "%44lx", 432 "%lx", 433 "%lX", 434 ]; 435 __gshared const uint[] ulong_val = [ 436 UINT_MAX, 437 91340, 438 341, 439 134, 440 131, 441 1, 442 0 443 ]; 444 __gshared const string[] llong_fmt = [ 445 "foo|%0123lld|bar", 446 "%123.9lld", 447 "% 123.9lld", 448 "%+123.9lld", 449 "%-123.9lld", 450 "%0123lld", 451 "% 0123lld", 452 "%+0123lld", 453 "%-0123lld", 454 "%5.5lld", 455 "%+22.33lld", 456 "%01.3lld", 457 "%1.5lld", 458 "%-1.5lld", 459 "%44lld", 460 "%lld", 461 ]; 462 __gshared const long[] llong_val = [ 463 LONG_MAX, 464 LONG_MIN, 465 - 91340, 466 91340, 467 341, 468 134, 469 131, 470 -1, 471 1, 472 0 473 ]; 474 __gshared const string[] string_fmt = [ 475 "foo|%10.10s|bar", 476 "%-10.10s", 477 "%10.10s", 478 "%10.5s", 479 "%5.10s", 480 "%10.1s", 481 "%1.10s", 482 "%10.0s", 483 "%0.10s", 484 "%-42.5s", 485 "%2.s", 486 "%.10s", 487 "%.1s", 488 "%.0s", 489 "%.s", 490 "%4s", 491 "%s", 492 ]; 493 __gshared const string[] string_val = [ 494 "Hello", 495 "Hello, world!", 496 "Sound check: One, two, three.", 497 "This string is a little longer than the other strings.", 498 "1", 499 "\0"[0..0], // D outputs null for empty strings, this is a workaround 500 null 501 ]; 502 __gshared const string[] pointer_fmt = [ 503 "foo|%p|bar", 504 "%42p", 505 "%p", 506 ]; 507 __gshared const string[]*[] pointer_val = [ 508 &pointer_fmt, 509 &string_fmt, 510 &string_val, 511 null 512 ]; 513 char[1024] buf1; 514 char[1024] buf2; 515 double digits = 9.123456789012345678901234567890123456789; 516 int failed = 0, num = 0; 517 518 void TEST(T)(const string[] fmt, T[] val) { 519 for (int i = 0; i < fmt.length; i++) 520 for (int j = 0; j < val.length; j++) { 521 static if (typeid(T) is typeid(const string)) { 522 int r1 = sprintf(buf1.ptr, fmt[i].ptr, val[j].ptr); 523 } else { 524 int r1 = sprintf(buf1.ptr, fmt[i].ptr, val[j]); 525 } 526 int r2 = snprintf(buf2, fmt[i], val[j]); 527 string a = r1 < 0 ? null : cast(string)buf1[0..r1]; 528 string b = r2 < 0 ? null : cast(string)buf2[0..r2]; 529 if (a != b || r1 != r2) { 530 printf("Results don't match, " 531 ~ "format string: %s\n" 532 ~ "\t sprintf(3): [%s] (%d)\n" 533 ~ "\tsnprintf(3): [%s] (%d)\n", 534 fmt[i], a, r1, b, r2); 535 failed++; 536 } 537 num++; 538 } 539 } 540 541 printf("Testing our snprintf(3) against your system's sprintf(3).\n"); 542 TEST(float_fmt, float_val); 543 TEST(long_fmt, long_val); 544 TEST(ulong_fmt, ulong_val); 545 TEST(llong_fmt, llong_val); 546 TEST(string_fmt, string_val); 547 TEST(pointer_fmt, pointer_val); 548 printf("Result: %d out of %d tests failed.\n", failed, num); 549 550 printf("Checking how many digits we support: "); 551 for (int i = 0; i < 100; i++) { 552 double value = pow(10.0, cast(double)i) * digits; 553 sprintf(buf1.ptr, "%.1f\n", value); 554 snprintf(buf2, "%.1f\n", value); 555 if (strcmp(buf1.ptr, buf2.ptr) != 0) { 556 printf("apparently %d.\n", i); 557 break; 558 } 559 } 560 return (failed == 0) ? 0 : 1; 561 } 562 563 }